#include "mainwindow.h"

#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
    ui->setupUi(this);
    this->setWindowTitle(QStringLiteral("UDP Multicast"));

    //工具栏
    ui->mainToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    m_pActJoinMulti = new QAction(QIcon(":/new/prefix1/res/添加群组.png"), QStringLiteral("加入组播"), this);
    m_pActLeaveMulti = new QAction(QIcon(":/new/prefix1/res/退出群组.png"), QStringLiteral("退出组播"), this);
    m_pActClearText = new QAction(QIcon(":/new/prefix1/res/清空.png"), QStringLiteral("清空文本"), this);
    m_pActQuit = new QAction(QIcon(":/new/prefix1/res/退出.png"), QStringLiteral("退出"), this);
    ui->mainToolBar->addAction(m_pActJoinMulti);
    ui->mainToolBar->addAction(m_pActLeaveMulti);
    ui->mainToolBar->addSeparator();
    ui->mainToolBar->addAction(m_pActClearText);
    ui->mainToolBar->addSeparator();
    ui->mainToolBar->addAction(m_pActQuit);

    //界面布局
    m_pCentralWidget = new QWidget(this);
    m_pLabPort = new QLabel(QStringLiteral("组播端口"), m_pCentralWidget);
    m_pSpinPort = new QSpinBox(m_pCentralWidget);
    m_pSpinPort->setMinimum(1);
    m_pSpinPort->setMaximum(65535);
    m_pSpinPort->setValue(3200);
    m_pLabAddr = new QLabel(QStringLiteral("组播地址"), m_pCentralWidget);
    m_pComboAddr = new QComboBox(m_pCentralWidget);
    m_pComboAddr->setEditable(true);  //下拉框可编辑输入
    m_pComboAddr->addItem("239.0.0.1");
    // 正则匹配 D类IP：224.0.0.0~239.255.255.255
    // .必须使用转义字符\，否则.会匹配任意字符
    // C++中"\"在字符串中表示要用"\\"
    // 是 - 不是 ~ ； 是[0-9]不是[0~9]
    QRegExp regExp("^(22[4-9]|23[0-9])(\\.((\\d)|([1-9]\\d)|(1\\d{2})|(2[0-4]\\d)|(25[0-5]))){3}$");
    QValidator* pValidator = new QRegExpValidator(regExp, this);
    m_pComboAddr->setValidator(pValidator);
    QHBoxLayout* HLay1 = new QHBoxLayout();
    HLay1->addWidget(m_pLabPort, 1, Qt::AlignRight);
    HLay1->addWidget(m_pSpinPort, 1);
    HLay1->addWidget(m_pLabAddr, 1, Qt::AlignRight);
    HLay1->addWidget(m_pComboAddr, 2);
    m_pLineEdit = new QLineEdit(m_pCentralWidget);
    m_pBtnSendMulti = new QPushButton(QStringLiteral("组播消息"), m_pCentralWidget);
    QHBoxLayout* HLay2 = new QHBoxLayout();
    HLay2->addWidget(m_pLineEdit, 4);
    HLay2->addWidget(m_pBtnSendMulti, 1);
    m_pPlainText = new QPlainTextEdit(m_pCentralWidget);
    QVBoxLayout* VLay = new QVBoxLayout(m_pCentralWidget);
    VLay->addLayout(HLay1);
    VLay->addLayout(HLay2);
    VLay->addWidget(m_pPlainText);
    this->setCentralWidget(m_pCentralWidget);
    this->setLayout(VLay);

    //状态栏
    m_pLabState = new QLabel(QStringLiteral("socket状态："), this);
    m_pLabState->setMinimumWidth(150);
    ui->statusBar->addWidget(m_pLabState);

    QString str = getLocalIP();
    this->setWindowTitle(this->windowTitle() + "---IP:" + str);

    m_pUdpSocket = new QUdpSocket(this);
    m_pUdpSocket->setSocketOption(QAbstractSocket::MulticastTtlOption, 1);

    // connect
    connect(m_pActJoinMulti, &QAction::triggered, this, &MainWindow::slotActJoinMulti);
    connect(m_pActLeaveMulti, &QAction::triggered, this, &MainWindow::slotActLeaveMulti);
    connect(m_pActClearText, &QAction::triggered, this, &MainWindow::slotActClearText);
    connect(m_pActQuit, &QAction::triggered, this, &MainWindow::slotActQuit);
    connect(m_pUdpSocket, &QUdpSocket::stateChanged, this, &MainWindow::slotSocketStateChanged);
    connect(m_pBtnSendMulti, &QPushButton::clicked, this, &MainWindow::slotBtnMultiMsg);
    connect(m_pUdpSocket, &QUdpSocket::readyRead, this, &MainWindow::slotReadyRead);
}

MainWindow::~MainWindow() { delete ui; }

void MainWindow::slotActJoinMulti() {
    QString ip = m_pComboAddr->currentText();
    m_multicastAddr = QHostAddress(ip);
    quint16 multicastPort = m_pSpinPort->value();
    if (m_pUdpSocket->bind(QHostAddress::AnyIPv4, multicastPort, QUdpSocket::ShareAddress)) {
        m_pUdpSocket->joinMulticastGroup(m_multicastAddr);  //加入多播组
        m_pPlainText->appendPlainText("**加入组播成功");
        m_pPlainText->appendPlainText("**组播地址IP：" + ip);
        m_pPlainText->appendPlainText("**绑定端口：" + QString::number(multicastPort));
        m_pActJoinMulti->setEnabled(false);
        m_pActLeaveMulti->setEnabled(true);
        m_pComboAddr->setEditable(false);
    } else {
        m_pPlainText->appendPlainText("**绑定端口失败");
    }
}

void MainWindow::slotActLeaveMulti() {
    m_pUdpSocket->leaveMulticastGroup(m_multicastAddr);  //退出组播
    m_pUdpSocket->abort();                               //解除绑定
    m_pActJoinMulti->setEnabled(true);
    m_pActLeaveMulti->setEnabled(false);
    m_pComboAddr->setEnabled(true);
    m_pComboAddr->setEditable(true);
    m_pPlainText->appendPlainText("**已退出组播，解除端口绑定");
}

void MainWindow::slotActClearText() { m_pPlainText->clear(); }

void MainWindow::slotActQuit() {
    QMessageBox::StandardButton button = QMessageBox::question(this, "", "是否退出？");
    if (QMessageBox::StandardButton::Yes == button) {
        this->close();
    }
}

void MainWindow::slotSocketStateChanged(QAbstractSocket::SocketState socketState) {
    // case并不包含所有的情况，因为没有写listening的情况，所以就需要写default
    switch (socketState) {
        case QAbstractSocket::UnconnectedState: m_pLabState->setText("socket状态：UnconnectedState"); break;
        case QAbstractSocket::HostLookupState: m_pLabState->setText("socket状态：HostLookupState"); break;
        case QAbstractSocket::ConnectingState: m_pLabState->setText("socket状态：ConnectingState"); break;
        case QAbstractSocket::ConnectedState: m_pLabState->setText("socket状态：ConnectedState"); break;
        case QAbstractSocket::BoundState: m_pLabState->setText("socket状态：BoundState"); break;
        case QAbstractSocket::ClosingState: m_pLabState->setText("socket状态：ClosingState"); break;
        default: break;
    }
}

void MainWindow::slotBtnMultiMsg() {
    QString msg = m_pLineEdit->text();
    QByteArray str = msg.toUtf8();
    quint16 multiPort = m_pSpinPort->value();
    m_pUdpSocket->writeDatagram(str, m_multicastAddr, multiPort);
    m_pPlainText->appendPlainText("[multicast] " + msg);
    m_pLineEdit->clear();
    m_pLineEdit->setFocus();
}

void MainWindow::slotReadyRead() {
    while (m_pUdpSocket->hasPendingDatagrams()) {
        QByteArray dataGram;
        dataGram.resize(m_pUdpSocket->pendingDatagramSize());
        QHostAddress peerAddr;
        quint16 peerPort;
        m_pUdpSocket->readDatagram(dataGram.data(), dataGram.size(), &peerAddr, &peerPort);
        QString str = dataGram.data();
        QString peer = "[From " + peerAddr.toString() + ":" + QString::number(peerPort) + "] ";
        m_pPlainText->appendPlainText(peer + str);
        qDebug() << m_pUdpSocket->peerAddress();
        qDebug() << m_pUdpSocket->localAddress().toString();
        qDebug() << m_pUdpSocket->localPort();
    }
}

QString MainWindow::getLocalIP() {
    QString localName = QHostInfo::localHostName();
    QHostInfo hostInfo = QHostInfo::fromName(localName);
    QList<QHostAddress> addrList = hostInfo.addresses();
    QString localIP = "";
    if (!addrList.isEmpty()) {
        for (int i = 0; i < addrList.size(); i++) {
            QHostAddress addr = addrList.at(i);
            if (QAbstractSocket::IPv4Protocol == addr.protocol()) {
                localIP = addr.toString();
                break;
            }
        }
    }
    return localIP;
}
